#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>

#include <unistd.h>

//#include <PPD.h> NO ANDA

//___________________________________________________
//____________VARIABLES Y ESTRUCTURAS________________
//___________________________________________________

typedef struct{
	char a[512];
}sectType;

typedef struct{
	int cilinder;
	int head;
	int sector;
}CHS;

typedef struct{
	long maxSector;
	sectType *buffer;
	sectType *pArchivo;
	void *lstSector;
	void *fstSector;
}searchType;

typedef struct{
	long sector;
	void *nextSector;//Apunta al prox de la lista
}listaSectoresLectura;

typedef struct{
	long sector;
	sectType data;
	void *nextSector;//Apunta al prox de la lista
}listaSectoresEscritura;

pthread_mutex_t  bufferSem= PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t  bVacioSem= PTHREAD_MUTEX_INITIALIZER;

//___________________________________________________
//________FIN VARIABLES Y ESTRUCTURAS________________
//___________________________________________________



//___________________________________________________
//___________________HEADERS_________________________
//___________________________________________________

long getSector(CHS dir,int cilinders,int heads,int sectors);

CHS getRealDir(long sector,int cilinders,int heads,int sectors);

int GetBitValue(int n, int bitPosition);

unsigned int valorEnBits(int n);

int openFile(char *path);

sectType* mapFile(int fileDescriptor, long fileSize, int mode);

long getSector(CHS dir,int cilinder,int head,int sector);

CHS getRealDir(long sector,int cilinders,int heads,int sectors);

void createSearchThread(pthread_t *thread,searchType *param);

void bringSector(long sector, void **lstSector);

void writeSector(long sector,sectType data, void **lstSector);

//___________________________________________________
//___________________FIN HEADERS_____________________
//___________________________________________________


#define FILEPATH "/home/utn_so/fat32.disk" //CAMBIAR PATH !
#define SECTORSIZE (512)
#define CLUSTERSIZE  (SECTORSIZE*8) //8 sectores por cluster = 4Kb
#define CANTSECT  (1048576) //Cantidad de sectores a traer
#define FILESIZE (524288*1024) //Segun el ejemplo del TP

//___________________________________________________
//___________________FUNCIONES_______________________
//___________________________________________________

int GetBitValue( int n, int bitPosition ) {
   return ((n >> bitPosition) & 1);
}

unsigned int valorEnBits(int n){
	int i;
	unsigned int result=0;
	for(i=0;i<8;i++){
		result+=GetBitValue(n,i);
		result*=10;
	}
	result/=10;
	return result;
}

long getSector(CHS dir,int cilinders,int heads,int sectors){
	return ((dir.cilinder*heads*sectors)+(sectors*dir.head)+(dir.sector-1));
}

CHS getRealDir(long sector,int cilinders,int heads,int sectors){
	CHS result;
	result.cilinder=sector/(heads*sectors);
	result.head=(sector%(heads*sectors))/sectors;
	result.sector=((sector%(heads*sectors))%sectors)+1;//Con codificacion sin sector 0;
	return result;
}


int openFile(char *path){
	int fileDescriptor;
	fileDescriptor = open(path, O_RDWR);
	if (fileDescriptor == -1) {
		perror("Error abriendo el archivo para lectura");
		exit(EXIT_FAILURE);
	}
	return fileDescriptor;
}

sectType* mapFile(int fileDescriptor, long fileSize, int mode){
	sectType *pArchivo;
	//pArchivo = mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fileDescriptor, 0);
	pArchivo = mmap(0, fileSize, mode, MAP_SHARED, fileDescriptor, 0);
	if (pArchivo == MAP_FAILED) {
		close(fileDescriptor);
		perror("Error mapeando el archivo");
		exit(EXIT_FAILURE);
	}
	return pArchivo;
}

//posix_madvise(0, size, POSIX_MADV_RANDOM );

long traerSectorNodo(void **ptrSector){
	listaSectoresLectura sectR;
	listaSectoresEscritura sectW;
	if(sizeof(*ptrSector)==sizeof(listaSectoresLectura)){
		sectR = (listaSectoresLectura)(*ptrSector);
		return sectR.sector;
	}else{
		sectW = (listaSectoresEscritura)(*ptrSector);
		return sectW.sector;
	}
}
void *traerPtrProxNodo(void **ptrSector){//Puedo hacer sin doble puntero porq no modifico el valor ?
	listaSectoresLectura *sectR;
	listaSectoresEscritura *sectW;
	if(sizeof(*ptrSector)==sizeof(listaSectoresLectura)){
		sectR = (listaSectoresLectura*)(*ptrSector);
		return &(sectR.nextSector);
	}else{
		sectW = (listaSectoresEscritura*)(*ptrSector);
		return sectW.nextSector;
	}
}

void asignarPtrProxNodo(void **ptrSector){
	listaSectoresLectura *sectR;
	listaSectoresEscritura *sectW;
	if(sizeof(*ptrSector)==sizeof(listaSectoresLectura)){
		sectR = (listaSectoresLectura *)(ptrSector);
		sectR.nextSector = traerPtrProxNodo(ptrSector);
	}else{
		sectW = (listaSectoresEscritura *)(ptrSector);
		*sectW.nextSector = traerPtrProxNodo(ptrSector);
	}
}

void eliminarNodo(long sectorBuscado,void **fstSector){
	void* ptrSector;
	void* ptrAux;

	//Verifica si es el primero
	if(traerSectorNodo(&fstSector)==sectorBuscado){
		ptrAux=fstSector;
		*fstSector=traerPtrProxNodo(&fstSector);
		free(ptrAux);
	}else{
		//Si no es el primero continua con el resto
		ptrSector=fstSector;
		while(ptrSector!=NULL){
			if(traerSectorNodo(traerPtrProxNodo(&ptrSector))==sectorBuscado){
				ptrAux=traerPtrProxNodo(&ptrSector);
				asignarPtrProxNodo(&ptrSector);
				free(ptrAux);
				ptrSector=NULL;
			}else{
				asignarPtrProxNodo(&ptrSector);
			}
		}
	}
}

void threadScan(void *threadarg){
	searchType *my_data;
	my_data = (searchType*)(threadarg);

	int flag=0;
	long sectActual=0;
	long n;
	sectType *buffer;

	listaSectoresLectura sectR;
	listaSectoresEscritura sectW;
	void *ptrSector;

	while(1){
		ptrSector=my_data->fstSector;
		if(*ptrSector!=NULL){
			if(sizeof(*ptrSector)==sizeof(listaSectoresLectura)){
				sectR = (listaSectoresLectura)(*ptrSector);
				if(sectR.sector==sectActual){
					*buffer=my_data->pArchivo[n];
					*(my_data->buffer)=buffer;
					//Lo elimino de la lista
					free(ptrSector);
					my_data->fstSector=sectR.nextSector;
					ptrSector=sectR.nextSector;
				}
			}else{
				sectW = (listaSectoresEscritura)(my_data->fstSector[n]);
				if(sectW.sector==sectActual){
					*buffer=my_data->pArchivo[n];
					my_data->buffer=buffer;
					//Lo elimino de la lista
					free(ptrSector);
					my_data->fstSector=sectR.nextSector;
					ptrSector=sectR.nextSector;
				}
			}
		}
//-------------------------------------------------------------------------------
		while(*ptrSector!=NULL){
			if(sizeof(*ptrSector)==sizeof(listaSectoresLectura)){
				sectR = (listaSectoresLectura)(*ptrSector);
				if(sectR.sector==sectActual){
					*buffer=my_data->pArchivo[n];
					*(my_data->buffer)=buffer;
					//Lo elimino de la lista
					ptrSector
				}
			}else{
				sectW = (listaSectoresEscritura)(my_data->fstSector[n]);
				if(sectW.sector==sectActual){
					*buffer=my_data->pArchivo[n];
					my_data->buffer=buffer;
				}
			}

		ptrSector
		}


		if(sectActual==0 || sectActual==my_data->maxSector){
			flag=flag & 1;
		}else{
			if(flag==0){
				sectActual++;
			}else{
				sectActual--;
			}
		}
		sleep(1);
	}
}

void createSearchThread(pthread_t *thread,searchType *param){
	int result;
	result = pthread_create(&thread, NULL,(void *) &threadScan, &param);
	if (result){
		printf("ERROR; return code from pthread_create() is %d\n", *thread);
		exit(-1);
	}
}

void bringSector(long sector, void **lstSector){
	//Se genera una pila de sectores a buscar
	listaSectoresLectura* newSector = (listaSectoresLectura*)malloc(sizeof(listaSectoresLectura));
	newSector->sector=sector;
	newSector->nextSector=NULL;
	lstSector=&newSector;
}

void writeSector(long sector,sectType data, void **lstSector){
	listaSectoresEscritura* newSector = (listaSectoresEscritura*)malloc(sizeof(listaSectoresEscritura));
	newSector->sector=sector;
	newSector->data=data;
	newSector->nextSector=NULL;
	lstSector=&newSector;
}

//___________________________________________________
//___________________FIN FUNCIONES___________________
//___________________________________________________



int main(void) {
	int archivo;
	int i,printThread;
	long sector;
	sectType *pArchivo;
	sectType buffer;
	void *lstSectores;
	searchType param;
	pthread_t searchThread;

	lstSectores=NULL;

	archivo=openFile(FILEPATH);
	pArchivo = mapFile(archivo, FILESIZE, PROT_READ);

	param.buffer=&buffer;
	param.maxSector=(FILESIZE/512);
	param.pArchivo=pArchivo;
	param.sectorList=lstSectores;

	createSearchThread(&searchThread,&param);
	//createPrintThread(&printThread);

	//buffer = bringSector(archivo,sector);

	while(sector!=((long)("\0"))){
		printf("Ingrese sector a buscar\n");
		scanf("%d",&sector);
		bringSector2(sector,lstSectores);
		for(i=0;i<10000;i++){
			printf("%d\n",buffer);
		}

		//writeSector(archivo,sector,buffer);
		//buffer = bringSector(archivo,sector);
		//printf("%d",buffer);
	}
	printf("\nFin");

	if (munmap(pArchivo,FILESIZE) == -1) {
		perror("Error desmapeando el archivo");
	}
	close(archivo);
	return 0;
}





//___________________________________________________
//________________FUNCIONES VIEJAS___________________
//___________________________________________________

//sectType bringSector(int fileDescriptor ,long sector);
//void writeSector(int fileDescriptor ,long sector, sectType datos);
/*
sectType bringSector(int fileDescriptor ,long sector){
	sectType *pArchivo;
	sectType datos;
	//int npagina = (sector*SECTORSIZE)/getpagesize();
	int npagina = (sector/8);
	//int spagina = (8*(sector*SECTORSIZE)/getpagesize());
	int spagina= sector -(8*npagina);

	printf("Numero de Pagina: %d\n", npagina);
	printf("Sector dentro de la pagina: %d\n", spagina);
	pArchivo = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, fileDescriptor, npagina*getpagesize());

	if (pArchivo == MAP_FAILED) {
		close(fileDescriptor);
		perror("Error mapeando el archivo");
		exit(EXIT_FAILURE);
	}
	datos=pArchivo[spagina];
	if (munmap(pArchivo,getpagesize()) == -1) {
		perror("Error desmapeando el archivo");
	}
	return datos;
}

void writeSector(int fileDescriptor ,long sector, sectType datos){
	sectType *pArchivo;
	int npagina = (sector/8);
	int spagina= sector -(8*npagina);

	printf("Numero de Pagina: %d\n", npagina);
	printf("Sector dentro de la pagina: %d\n", spagina);
	pArchivo = mmap(0, getpagesize(), PROT_WRITE, MAP_SHARED, fileDescriptor, npagina*getpagesize());

	if (pArchivo == MAP_FAILED) {
		close(fileDescriptor);
		perror("Error mapeando el archivo");
		exit(EXIT_FAILURE);
	}
	pArchivo[spagina]=datos;
	if (munmap(pArchivo,getpagesize()) == -1) {
		perror("Error desmapeando el archivo");
	}
}
void asignarPtrProxNodo(void *ptrSector, void *ptrProxNodo){
	listaSectoresLectura *sectR;
	listaSectoresEscritura *sectW;
	if(sizeof(*ptrSector)==sizeof(listaSectoresLectura)){
		sectR = (listaSectoresLectura *)(ptrSector);
		sectR.nextSector = ptrProxNodo;
	}else{
		sectW = (listaSectoresEscritura *)(ptrSector);
		sectW.nextSector = ptrProxNodo;
	}
}

*/
